package com.security.springcloud.uaa.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.Arrays;

/**
 * @author yingyuwei
 * @version 1.0.0
 * @description 授权服务
 * 文档参考 https://projects.spring.io/spring-security-oauth/docs/oauth2.html
 * @createTime 2021年03月01日 16:41:00
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private ClientDetailsService clientDetailsService;
    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Autowired
    private PasswordEncoder passwordEncoder;

    //可以通过直接访问基础存储（例如的情况下为数据库表JdbcClientDetailsService）或通过ClientDetailsManager接口（这两种实现都ClientDetailsService可以实现）
    //来更新正在运行的应用程序中的客户端详细信息。
    @Bean
    public ClientDetailsService clientDetailsService(DataSource dataSource) {
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        clientDetailsService.setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }


    //定义客户端详细信息服务的配置程序
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService);
//        clients
//                .inMemory()
//                .withClient("c1") //客户端ID。
//                .secret(new BCryptPasswordEncoder().encode("secret")) //对于受信任的客户端是必需的）客户端密钥（如果有）。
//                .resourceIds("res1")
//                .authorizedGrantTypes("authorization_code" , "password"
//                        , "client_credentials" , "implicit" , "refresh_token") //授权客户使用的授权类型。默认值为空。
//                .scopes("all") //客户端受限制的范围。如果范围未定义或为空（默认值），则客户端不受范围的限制。
//                .autoApprove(false)
//                .redirectUris("http://www.baidu.com"); //加上验证回调地址
    }


    //定义令牌端点上的安全性约束
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()")  //tokenkey这个endpoint当使用JwtToken且使用非对称加密时，资源服务用于获取公钥而开放的，这里指这个 endpoint完全公开。
                .checkTokenAccess("permitAll()")    //checkToken这个endpoint完全公开
                .allowFormAuthenticationForClients(); //允许表单认证
    }

    //定义授权和令牌端点以及令牌服务
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager) //密码授予通过注入来打开AuthenticationManager
                .authorizationCodeServices(authorizationCodeServices) //定义AuthorizationCodeServices用于授权码授予的授权码服务（的实例）
                .tokenServices(tokenServices())
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);

    }

    //管理令牌
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        //创建访问令牌后，必须存储身份验证，以便接受访问令牌的资源以后可以引用它。
        //访问令牌用于加载用于授权其创建的身份验证
        DefaultTokenServices service = new DefaultTokenServices();
        service.setClientDetailsService(clientDetailsService); //客户端详细信息服务，用于查找客户端（如有必要）。
        service.setSupportRefreshToken(true);   //是否支持刷新令牌。
        service.setTokenStore(tokenStore);      //令牌存储的持久性策略
        service.setAccessTokenValiditySeconds(7200);    //访问令牌的默认有效性（以秒为单位）。
        service.setRefreshTokenValiditySeconds(259200); //刷新令牌的有效性（以秒为单位)

        //令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));
        service.setTokenEnhancer(tokenEnhancerChain);

        return service;
    }


    //用于授权码授予的授权码服务
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
        //设置授权码模式的授权码如何存取，暂时采用内存方式
        return new JdbcAuthorizationCodeServices(dataSource);
    }
//    @Bean
//    public AuthorizationCodeServices authorizationCodeServices() {
//        //设置授权码模式的授权码如何存取，暂时采用内存方式
//        return new InMemoryAuthorizationCodeServices();
//    }






}
